#ifndef Abckit_Wrapper_H
#define Abckit_Wrapper_H

#include "c/extensions/arkts/metadata_arkts.h"
#include "c/ir_core.h"
#include "c/isa/isa_dynamic.h"
#include "c/metadata_core.h"
#include "memory"
#include <variant>
#include "abckit_metadata.h"
#include <functional>

using InstUserCallback = std::function<bool(AbckitInst *)>;

struct AbckitApiAdapter
{
    struct
    {
        AbckitFile *(*openAbc)(const std::string &path);
        void (*writeAbc)(AbckitFile *file, const std::string &path);
        void (*closeFile)(AbckitFile *file);
    } io;

    struct
    {
        bool (*fileEnumerateModules)(AbckitFile *file, void *data, bool (*cb)(AbckitCoreModule *module, void *data));
        bool (*moduleEnumerateNamespaces)(AbckitCoreModule *m, void *data, bool (*cb)(AbckitCoreNamespace *n, void *data));
        bool (*moduleEnumerateClasses)(AbckitCoreModule *m, void *data, bool (*cb)(AbckitCoreClass *klass, void *data));
        bool (*moduleEnumerateTopLevelFunctions)(AbckitCoreModule *m, void *data, bool (*cb)(AbckitCoreFunction *function, void *data));
        bool (*namespaceEnumerateNamespaces)(AbckitCoreNamespace *n, void *data, bool (*cb)(AbckitCoreNamespace *klass, void *data));
        bool (*namespaceEnumerateClasses)(AbckitCoreNamespace *n, void *data, bool (*cb)(AbckitCoreClass *klass, void *data));
        bool (*namespaceEnumerateTopLevelFunctions)(AbckitCoreNamespace *n, void *data, bool (*cb)(AbckitCoreFunction *function, void *data));
        bool (*classEnumerateMethods)(AbckitCoreClass *klass, void *data, bool (*cb)(AbckitCoreFunction *method, void *data));
        bool (*isModuleNode)(abc::CoreNode coreNode);
        bool (*isNamespaceNode)(abc::CoreNode coreNode);
        bool (*isClassNode)(abc::CoreNode coreNode);
        bool (*isFunctionNode)(abc::CoreNode coreNode);
        abc::AbcNodeType (*getNodeType)(abc::CoreNode coreNode);
    } node;

    struct
    {
        bool (*moduleEnumerateImports)(AbckitCoreModule *m, void *data, bool (*cb)(AbckitCoreImportDescriptor *i, void *data));
        AbckitCoreModule *(*importDescriptorGetImportedModule)(AbckitCoreImportDescriptor *i);
        AbckitCoreImportDescriptor *(*iGetImportDescriptor)(AbckitInst *inst);
    } import;

    struct
    {
        bool (*functionIsStatic)(AbckitCoreFunction *function);
        bool (*functionIsAnonymous)(AbckitCoreFunction *function);
        AbckitCoreModule *(*functionGetModule)(AbckitCoreFunction *function);
        AbckitFile *(*functionGetFile)(AbckitCoreFunction *function);
        void (*functionSetGraph)(AbckitCoreFunction *function, AbckitGraph *graph);
    } function;

    struct
    {
        const char *(*abckitStringToString)(AbckitString *value);
        AbckitString *(*moduleGetName)(AbckitCoreModule *m);
        AbckitString *(*namespaceGetName)(AbckitCoreNamespace *n);
        AbckitString *(*classGetName)(AbckitCoreClass *klass);
        AbckitString *(*functionGetName)(AbckitCoreFunction *function);
        AbckitString *(*importDescriptorGetName)(AbckitCoreImportDescriptor *i);
        AbckitString *(*createString)(AbckitFile *file, const char *value, size_t len);
        std::string (*getModuleName)(AbckitCoreModule *m);
        std::string (*getNamespaceName)(AbckitCoreNamespace *n);
        std::string (*getClassName)(AbckitCoreClass *klass);
        std::string (*getFunctionName)(AbckitCoreFunction *function);
        std::string (*getNodeName)(abc::CoreNode coreNode);
        std::string (*getNodePath)(abc::AbcNodePtr abcNode);
        std::string (*getFuncPath)(abc::AbcFunctionPtr abcFunction);
        std::string (*getImportDescriptorName)(AbckitCoreImportDescriptor *i);
        AbckitString *(*iGetString)(AbckitInst *inst);
        std::string (*getInstName)(AbckitInst *inst);
    } string;

    struct
    {
        AbckitGraph *(*createGraphFromFunction)(AbckitCoreFunction *function);
        void (*destroyGraph)(AbckitGraph *graph);
        bool (*gVisitBlocksRpo)(AbckitGraph *graph, void *data, bool (*cb)(AbckitBasicBlock *basicBlock, void *data));
    } graph;

    struct
    {
        AbckitGraph *(*bbGetGraph)(AbckitBasicBlock *basicBlock);
        AbckitInst *(*bbGetFirstInst)(AbckitBasicBlock *basicBlock);
        AbckitInst *(*iGetPrev)(AbckitInst *inst);
        AbckitInst *(*iGetNext)(AbckitInst *inst);
        AbckitCoreFunction *(*iGetFunction)(AbckitInst *inst);
        enum AbckitIsaApiDynamicOpcode (*iGetOpcode)(AbckitInst *inst);
        std::string (*iGetOpcodeType)(AbckitIsaApiDynamicOpcode opcode);
        AbckitBasicBlock *(*getFunctionStartBlock)(AbckitGraph *graph);
        void (*bbAddInstFront)(AbckitBasicBlock *basicBlock, AbckitInst *inst);
        void (*iInsertBefore)(AbckitInst *newInst, AbckitInst *ref);
        void (*iInsertAfter)(AbckitInst *newInst, AbckitInst *ref);
        AbckitBasicBlock *(*gGetStartBasicBlock)(AbckitGraph *graph);
        bool (*bbVisitSuccBlocks)(AbckitBasicBlock *basicBlock, void *data, bool (*cb)(AbckitBasicBlock *succBasicBlock, void *data));
        bool (*iVisitUsers)(AbckitInst *inst, void *data, bool (*cb)(AbckitInst *user, void *data));
        void (*enumerateInstUsers)(AbckitInst *inst, const InstUserCallback &cb);
    } inst;

    struct
    {
        AbckitInst *(*iCreateLdexternalmodulevar)(AbckitGraph *graph, AbckitCoreImportDescriptor *id);
        AbckitInst *(*iCreateThrowUndefinedifholewithname)(AbckitGraph *graph, AbckitInst *acc, AbckitString *string);
        AbckitInst *(*iCreateLdobjbyname)(AbckitGraph *graph, AbckitInst *acc, AbckitString *string);
        AbckitInst *(*iCreateCallthis0)(AbckitGraph *graph, AbckitInst *acc, AbckitInst *input0);
    } isa;
};

struct AbckitApiAdapter const *createAbckitAdapter(abc::AbckitVersion version = abc::AbckitVersion::LATEST);

#endif